﻿// Necroz project team © <2015> <ArchLord2EMU>
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Author:Sagara
// Date:2015-09-13-22:46

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Xml.Serialization;
using AL2Login.Configs;
using AL2Login.Interfaces;
using AL2Login.Models;
using ArchLord2Commons.IoC;
using ArchLord2Commons.Models;
using ArchLord2Commons.Network.Pipe;
using CM.Framework.Network.RMI;
using NLog;

namespace AL2Login.Services
{
    class GameServerPipeService : IGsPipeService
    {
        private static readonly Logger Log = LogManager.GetLogger(typeof(GameServerPipeService).Name);

        private static readonly List<Gameserver> Gameservers = new List<Gameserver>();
        private static readonly object GsLock = new object();

        public void Initilize()
        {
            GameServerModel infos;
            using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(NetworkCfg.Default.GameServers)))
                infos = (GameServerModel)new XmlSerializer(typeof(GameServerModel)).Deserialize(ms);

            foreach (var gameserverInfo in infos.GameServers)
            {
                var svc = new RMIPipe<IRMIGameBridge>(new GameServerPipeService(), 2000);
                svc.Listen(new IPEndPoint(IPAddress.Parse(gameserverInfo.CommunicationIp),
                    gameserverInfo.CommunicationPort), gameserverInfo.TrustedAddresses.Select(ad => ad.Ip).ToArray());

                var gs = new Gameserver { Id = gameserverInfo.Id, Pipe = svc };
                svc.OnDisconnected += () =>
                {
                    gs.Connected = false;

                    Log.Info("Gameserver {0}:[{1}] disconnected!", gs.Id, gs.Name);
                };

                lock (GsLock)
                    Gameservers.Add(gs);

                Log.Debug("Listening for gameserver '{0}' on '{1}:{2}'", gameserverInfo.Id,
                    gameserverInfo.CommunicationIp, gameserverInfo.CommunicationPort);

            }
        }

        public static List<GsInfo> GetRealInfo()
        {
            List<GsInfo> infos = new List<GsInfo>();

            lock (GsLock)
                infos.AddRange(Gameservers.Select(gameserver => new GsInfo(gameserver.Id, gameserver.Name, 0, "Remote adresses")));
            
            return infos;
        } 

        public void Release()
        {
            
        }

        internal class Gameserver
        {
            public RMIPipe<IRMIGameBridge> Pipe;
            public byte Id;
            public string Name;
            public bool Connected;
        }

        public bool SetRealmInfo(int realmId, string name)
        {
            Gameserver gs;
            lock (GsLock)
                gs = Gameservers.FirstOrDefault(gss => gss.Id == realmId);

            if (gs == null)
                return false;

            gs.Connected = true;
            gs.Name = name;

            Log.Info("Gameserver {0}:[{1}] connected!", realmId, gs.Name);

            return true;
        }

        public AccountData GetAccountInfo(string token, byte serverId)
        {
            using (var db = Global.GetMember<IDatabaseService>().GetFactory().OpenSession())
            {
                var model = db.QueryOver<UserModel>().Where(p => p.AccessToken == token).Take(1).SingleOrDefault();
                if (model == null)
                {
                    //User not found error!

                    return null;
                }

                var accInfo = new AccountData
                {
                    Id = model.Identity,
                    Login = model.UserName,
                    Password = model.Password,
                    Token = model.AccessToken,
                    ServerId = serverId
                };

                return accInfo;
            }
        }

        public void DisconnectMemberOnGameServer(int accountId, int serverId)
        {
            lock (GsLock)
            {
                var gs = Gameservers.First(s => s.Id == serverId);
                if(gs == null)
                    throw new Exception($"Can`t disconnect member on server: {serverId}, server not found..");

                if(gs.Pipe.RemoteService.IsConnected(accountId))
                   gs.Pipe.RemoteService.DisconnectAccount(accountId);
            }
        }

        public void NotifyAccountDisconnected(int accountId)
        {
            Global.GetMember<IAuthorizeService>().ClientDisconnected(accountId);
        }
    }
}
